#define DDCLLUA_CORE

#include "ddclsha2.h"
#include "lua-ddcl.h"

static int
l_encode (lua_State * L){
    size_t len;
    const dduint8 * str = luaL_checklstring(L, 1, &len);
    ddcl_SHA2Ctx ctx;
    ddcl_init_sha2(&ctx);
    ddcl_update_sha2(&ctx, str, len);
    dduint8 digest[DDCL_SHA2_DIGEST_SIZE];
    ddcl_final_sha2(&ctx, digest);
    lua_pushlstring(L, digest, DDCL_SHA2_DIGEST_SIZE);
    return 1;
}

static int
l_tostring (lua_State * L){
    size_t len;
    const dduint8 * digest = luaL_checklstring(L, 1, &len);
    if(len != DDCL_SHA2_DIGEST_SIZE){
        return luaL_error(L, "Invalid sha1 digest");
    }
    dduint8 result[DDCL_SHA2_STRING_SIZE];
    ddcl_tostring_sha2(digest, result);
    lua_pushstring(L, result);
    return 1;
}

static int
l_ctx (lua_State * L){
    ddcl_SHA2Ctx * ctx = (ddcl_SHA2Ctx *)lua_newuserdata(L, sizeof(ddcl_SHA2Ctx));
    ddcl_init_sha2(ctx);
    return 1;
}

static int
l_update (lua_State * L){
    ddcl_SHA2Ctx * ctx = lua_touserdata(L, 1);
    if(!ctx){
        return luaL_error(L, "userdata expect, got NULL");
    }
    size_t len;
    const dduint8 * str = luaL_checklstring(L, 2, &len);
    ddcl_update_sha2(ctx, str, len);
    return 0;
}

static int
l_final (lua_State * L){
    ddcl_SHA2Ctx * ctx = lua_touserdata(L, 1);
    if(!ctx){
        return luaL_error(L, "userdata expect, got NULL");
    }
    dduint8 digest[DDCL_SHA2_DIGEST_SIZE];
    ddcl_final_sha2(ctx, digest);
    lua_pushlstring(L, digest, DDCL_SHA2_DIGEST_SIZE);
    return 2;
}


static luaL_Reg _REG[] = {
    { NULL, NULL },
};

int
luaopen_ddcl_sha2 (lua_State * L){
    luaL_checkversion(L);
    lua_newtable(L);
    luapush_ddcl_sha2(L, -1);
    return 1;
}

int
luapush_ddcl_sha2 (lua_State * L, int i){
    lua_checkstack(L, 3);
    lua_pushvalue(L, i);
    DDLUA_PUSHFUNC(L, "encode", l_encode);
    DDLUA_PUSHFUNC(L, "tostring", l_tostring);
    DDLUA_PUSHFUNC(L, "ctx", l_ctx);
    DDLUA_PUSHFUNC(L, "update", l_update);
    DDLUA_PUSHFUNC(L, "final", l_final);
    lua_pop(L, 1);
    return 0;
}
